﻿using System;
using System.Linq;
using System.Linq.Expressions;
using System.Reflection;
using System.Collections.Generic;
using System.Reflection.Emit;

namespace Skynet.CommonLibrary.Extension
{
    /// <summary>
    /// 反射相关扩展
    /// </summary>
    ///<remarks>
    /// 作者：         李志强
    /// 开发时间：  2011-03-30
    /// </remarks>
    public static class ReflectionExtension
    {
        //private static void SetTypeValue(this PropertyInfo info, object obj, string fieldValue)
        //{
        //    if (info == null || !info.CanWrite || string.IsNullOrEmpty(fieldValue))
        //        return;

        //    if (IsType(info.PropertyType, "System.String"))
        //    {
        //        info.SetValue(obj, fieldValue, null);
        //    }
        //    if (IsType(info.PropertyType, "System.Int32"))
        //    {
        //        if (string.IsNullOrEmpty(fieldValue))
        //        {
        //            info.SetValue(obj, 0, null);
        //        }
        //        else
        //        {
        //            info.SetValue(obj, Int32.Parse(fieldValue), null);
        //        }
        //    }
        //    if (IsType(info.PropertyType, "System.DateTime"))
        //    {
        //        DateTime dt;
        //        DateTime.TryParse(fieldValue, out  dt);
        //        info.SetValue(obj, dt, null);
        //    }
        //    if (IsType(info.PropertyType, "System.Enum"))
        //    {
        //        int enValue = 0;
        //        foreach (string filed in fieldValue.Split(new char[] { '|' }, StringSplitOptions.RemoveEmptyEntries))
        //        {
        //            enValue = enValue | (int)Enum.Parse(info.PropertyType, filed);
        //        }
        //        info.SetValue(obj, enValue, null);
        //    }
        //}
        //public static void SetTypeValue(this PropertyInfo info, object obj, object fieldValue)
        //{
        //    if (info == null || !info.CanWrite || fieldValue == null)
        //        return;
        //    if (info.PropertyType.GetInterface(typeof(IList).FullName) != null)
        //    {
        //        info.SetValue(obj, fieldValue, null);
        //    }
        //    else
        //    {
        //        info.SetValue(obj, fieldValue, null);
        //    }
        //}

        //public static void SetValue(this object entity, string fieldName, string fieldValue)
        //{
        //    PropertyInfo info = entity.GetType().GetProperty(fieldName, BindingFlags.Public | BindingFlags.Instance | BindingFlags.IgnoreCase);
        //    info.SetTypeValue(entity, fieldValue);
        //}
        //public static void SetValue(this object entity, string fieldName, object fieldValue)
        //{
        //    PropertyInfo info = entity.GetType().GetProperty(fieldName, BindingFlags.Public | BindingFlags.Instance | BindingFlags.IgnoreCase);
        //    info.SetTypeValue(entity, fieldValue);
        //}

        //private static bool IsType(Type type, string typeName)
        //{
        //    if (type.ToString() == typeName)
        //        return true;
        //    if (type.ToString() == "System.Object")
        //        return false;

        //    return IsType(type.BaseType, typeName);
        //}


        /// <summary>
        /// 根据参数返回方法的 MethodInfo
        /// </summary>
        /// <param name="objType"> </param>
        /// <param name="methodName"></param>
        /// <param name="values"></param>
        /// <param name="isOut"></param>
        /// <returns></returns>
        public static MethodInfo GetOverLoadMethodInfo(this Type objType, string methodName, object[] values, bool isOut)
        {
            List<Type> typeList = new List<Type>();
            if (values != null && values.Length > 0)
            {
                typeList.AddRange(values.Select(o => o.GetType()));
            }
            if (isOut)
            {
                typeList.Add(Type.GetType("System.Int32&"));
            }
            return objType.GetMethod(methodName,
                BindingFlags.Instance | BindingFlags.Public | BindingFlags.IgnoreCase,
                null,
                typeList.Count > 0 ? typeList.ToArray() : Type.EmptyTypes,
                null);
        }
        /// <summary>
        /// 不考虑大小写 获取属性
        /// </summary>
        /// <param name="type">类型</param>
        /// <param name="name">属性名称</param>
        /// <returns></returns>
        public static PropertyInfo GetIgnPropertyInfo(this Type type, string name)
        {
            return type.GetProperty(name, BindingFlags.Public | BindingFlags.Instance | BindingFlags.IgnoreCase);
        }

        /// <summary>
        /// 获得真实类型（对Nullable进行了筛选）
        /// </summary>
        /// <param name="info"></param>
        /// <returns></returns>
        public static Type GetPropertyType(this PropertyInfo info)
        {
            Type pType = info.PropertyType;
            if (pType.IsGenericType && pType.GetGenericTypeDefinition() == typeof(Nullable<>))
            {
                return pType.GetGenericArguments()[0];
            }
            return pType;
        }

      


        /// <summary>
        /// 将实体a的属性赋值到实体b中，如果b中不存在a的属性则跳过
        /// </summary>
        /// <typeparam name="T1"></typeparam>
        /// <typeparam name="T2"></typeparam>
        /// <param name="a"></param>
        /// <param name="b"></param>
        public static void EntityConvert<T1, T2>(T1 a, T2 b)
            where T1 : class, new()
            where T2 : class, new()
        {
            Type t1 = a.GetType();
            foreach (PropertyInfo property in t1.GetProperties(BindingFlags.Public | BindingFlags.Instance | BindingFlags.IgnoreCase))
            {
                object obj = t1.GetValue(a, property.Name);
                if(obj ==null)
                    continue;
                SetValue(b, property.Name, obj);
            }
        }

        #region Expression
        #region SetValue
        public static void SetValue<T>(this object obj, string methodName, T methodValue)
        {
            Type objType = obj.GetType();
            Action<object, object> objAction;
            if (ExtensionCache.IsCacheExist(objType.Name + methodName))
            {
                objAction = ExtensionCache.GetCache<Action<object, object>>(objType.Name + methodName);
            }
            else
            {
                MethodInfo method = objType.GetMethod("set_" + methodName, BindingFlags.Public | BindingFlags.Instance | BindingFlags.IgnoreCase);
                if (method == null)
                    return;
                //判断是否为 可空类型
                Type type = method.GetParameters()[0].ParameterType;
                //NullableConverter nc = new System.ComponentModel.NullableConverter(type);
                //if (nc != null)
                //{
                //    type = nc.UnderlyingType;
                //}
               
                var paramObj = Expression.Parameter(typeof(object), "obj");
                var paramValue = Expression.Parameter(typeof(object), "value");

                var convertObj = Expression.Convert(paramObj, method.ReflectedType);
                var convertValue = Expression.Convert(paramValue, type);

                var callExpression = Expression.Call(convertObj, method, convertValue);
                Expression<Action<object, object>> lambdaExpression = Expression.Lambda<Action<object, object>>(callExpression, paramObj, paramValue);
                var action = lambdaExpression.Compile();
                objAction = (o1, o2) => action.SetTypeValue(o1, type, o2);
                ExtensionCache.SetCache(objType.Name + methodName, objAction,10);
            }
            objAction(obj, methodValue);
        }

        private static void SetTypeValue(this  Action<object, object> setAction, object entity, Type methodType, object methodValue)
        {
            try
            {

                //如果是枚举类型，考虑与计算的情况   a | b
                if (methodType.IsEnum)
                {
                    int enValue = methodValue.ToString().Split(new char[] {'|'}, StringSplitOptions.RemoveEmptyEntries).Aggregate(0, (current, filed) => current | (int) Enum.Parse(methodType, filed));
                    setAction(entity, enValue);
                    return;
                }

                //如果是值类型  进行强制转换
                if (methodValue.ToString() == string.Empty)
                {
                    return;
                }
                if (!methodType.IsGenericType && methodType.IsValueType)
                {
                    setAction(entity, methodType.GetMethod("Parse", new Type[] { typeof(string) }).Invoke(null, new object[] { methodValue.ToString() }));
                }
               //object类型 直接赋值
                else
                {
                    setAction(entity, methodValue);
                }
            }
            catch
            {
                if (methodType.BaseType != null)
                    throw new Exception(string.Format("\"{0}\"  格式 \"{1}\" 转换失败，请检查。值为:{2}",methodType.BaseType.Name, methodType.Name , methodValue));
            }
        }
        #endregion

        #region GetValue
        public static object GetValue(this Type objType,object entity, string methodName)
        {
            Func<object,object> objAction;
            if (ExtensionCache.IsCacheExist(objType.Name + methodName))
            {
                objAction = ExtensionCache.GetCache<Func<object, object>>(objType.Name + methodName);
            }
            else
            {
                MethodInfo method = objType.GetMethod("get_" + methodName, BindingFlags.Public | BindingFlags.Instance | BindingFlags.IgnoreCase);
                if (method == null)
                    return null;
              
                    var paramObj = Expression.Parameter(typeof(object), "obj");
                    var convertObj = Expression.Convert(paramObj, method.ReflectedType);
                    var callExpression = Expression.Call(convertObj, method);

                    var castMethodCall = Expression.Convert(callExpression, typeof(object));
                    Expression<Func<object, object>> lambdaExpression = Expression.Lambda<Func<object, object>>(castMethodCall, paramObj);
                    objAction = lambdaExpression.Compile();

                    ExtensionCache.SetCache(objType.Name + methodName, objAction, 10);
              
            }
            return  objAction(entity);
        }
        #endregion

        #region CreateInstance
        public static T CreateInstance<T>(this Type type)
        {
            return (T)type.CreateInstance();
        }
        public static object CreateInstance(this Type type)
        {
            if (ExtensionCache.IsCacheExist(type.FullName))
            {
                return ExtensionCache.GetCache < Func<object>>(type.FullName)();
            }
            NewExpression newExp = Expression.New(type);
            Expression<Func<object>> lambdaExp = Expression.Lambda<Func<object>>(newExp, null);
            Func<object> func = lambdaExp.Compile();
            ExtensionCache.SetCache(type.FullName, func,10);
            return func();
        }
        public static T CreateInstance<T>(this Type type, Type[] parameterTypes, object[] objs)
        {
            if (HttpRuntimeCache.IsCacheExist(type.FullName))
            {
                return (T)HttpRuntimeCache.GetCache<Func<object[], object>>(type.FullName)(objs);
            }
            //根据参数类型数组来获取构造函数
            var constructor = type.GetConstructor(parameterTypes);
            //创建lambda表达式的参数
            var lambdaParam = Expression.Parameter(typeof(object[]), "_args");
            //创建构造函数的参数表达式数组
            var constructorParam = new List<Expression>();
            for (int i = 0; i < parameterTypes.Length; i++)
            {
                //从参数表达式（参数是：object[]）中取出参数
                var arg = Expression.ArrayIndex(lambdaParam, Expression.Constant(i));
                //把参数转化成指定类型
                var argCast = Expression.Convert(arg, parameterTypes[i]);

                constructorParam.Add(argCast);
            }
            //创建构造函数表达式
            if (constructor != null)
            {
                NewExpression newExp = Expression.New(constructor, constructorParam);
                //创建lambda表达式，返回构造函数
                Expression<Func<object[], object>> lambdaExp =
                    Expression.Lambda<Func<object[], object>>(newExp, lambdaParam);

                Func<object[], object> func = lambdaExp.Compile();

                HttpRuntimeCache.SetCache(type.FullName, func, 10);
                return (T)func(objs);
            }
            return default(T);
        }

        /// <param name="assemblyPath">***.dll</param>
        /// <param name="className">namespace + . +className</param>
        /// <param name="parameterTypes">构造函数参数类别</param>
        /// <param name="objs">构造函数参数</param>
        public static T CreateInstance<T>(string assemblyPath, string className, Type[] parameterTypes, object[] objs)
        {
            Assembly assembly = Assembly.LoadFile(assemblyPath);
            return assembly.GetType(className, true, true).CreateInstance<T>(parameterTypes, objs);
        }
        /// <param name="assemblyPath">***.dll</param>
        /// <param name="className">namespace + className</param>
        public static T CreateInstance<T>(string assemblyPath, string className)
        {
            Assembly assembly = Assembly.LoadFile(assemblyPath);
            return assembly.GetType(className, true, true).CreateInstance<T>();
        }
        public static T CreateInstanceEx<T>(this Assembly assembly, string className)
        {
            return assembly.GetType(className,true,true).CreateInstance<T>();
        }
        #endregion

        #region method
        public static Delegate MethodInveke<T>(this Type type, string methodName)
        {
            MethodInfo methodInfo = type.GetMethod(methodName);
            if (methodInfo == null)
                throw new Exception("找不到此方法");
            return methodInfo.GetMethodInvoker<T>();
        }
        public static Delegate GetMethodInvoker<T>(this MethodInfo methodInfo)
        {
            if (methodInfo.DeclaringType != null)
            {
                DynamicMethod dynamicMethod = new DynamicMethod(string.Empty, typeof(object), new Type[] { typeof(object), typeof(object[]) }, methodInfo.DeclaringType.Module);
                ILGenerator il = dynamicMethod.GetILGenerator();
                ParameterInfo[] ps = methodInfo.GetParameters();
                Type[] paramTypes = new Type[ps.Length];
                for (int i = 0; i < paramTypes.Length; i++)
                {
                    if (ps[i].ParameterType.IsByRef)
                        paramTypes[i] = ps[i].ParameterType.GetElementType();
                    else
                        paramTypes[i] = ps[i].ParameterType;
                }
                LocalBuilder[] locals = new LocalBuilder[paramTypes.Length];

                for (int i = 0; i < paramTypes.Length; i++)
                {
                    locals[i] = il.DeclareLocal(paramTypes[i], true);
                }
                for (int i = 0; i < paramTypes.Length; i++)
                {
                    il.Emit(OpCodes.Ldarg_1);
                    EmitFastInt(il, i);
                    il.Emit(OpCodes.Ldelem_Ref);
                    EmitCastToReference(il, paramTypes[i]);
                    il.Emit(OpCodes.Stloc, locals[i]);
                }
                if (!methodInfo.IsStatic)
                {
                    il.Emit(OpCodes.Ldarg_0);
                }
                for (int i = 0; i < paramTypes.Length; i++)
                {
                    il.Emit(ps[i].ParameterType.IsByRef ? OpCodes.Ldloca_S : OpCodes.Ldloc, locals[i]);
                }
                il.EmitCall(methodInfo.IsStatic ? OpCodes.Call : OpCodes.Callvirt, methodInfo, null);
                if (methodInfo.ReturnType == typeof(void))
                    il.Emit(OpCodes.Ldnull);
                else
                    EmitBoxIfNeeded(il, methodInfo.ReturnType);

                for (int i = 0; i < paramTypes.Length; i++)
                {
                    if (ps[i].ParameterType.IsByRef)
                    {
                        il.Emit(OpCodes.Ldarg_1);
                        EmitFastInt(il, i);
                        il.Emit(OpCodes.Ldloc, locals[i]);
                        if (locals[i].LocalType.IsValueType)
                            il.Emit(OpCodes.Box, locals[i].LocalType);
                        il.Emit(OpCodes.Stelem_Ref);
                    }
                }

                il.Emit(OpCodes.Ret);
                Delegate invoder = dynamicMethod.CreateDelegate(typeof(T));
                return invoder;
            }
            return null;
        }

        private static void EmitCastToReference(ILGenerator il, Type type)
        {
            il.Emit(type.IsValueType ? OpCodes.Unbox_Any : OpCodes.Castclass, type);
        }

        private static void EmitBoxIfNeeded(ILGenerator il, Type type)
        {
            if (type.IsValueType)
            {
                il.Emit(OpCodes.Box, type);
            }
        }

        private static void EmitFastInt(ILGenerator il, int value)
        {
            switch (value)
            {
                case -1:
                    il.Emit(OpCodes.Ldc_I4_M1);
                    return;
                case 0:
                    il.Emit(OpCodes.Ldc_I4_0);
                    return;
                case 1:
                    il.Emit(OpCodes.Ldc_I4_1);
                    return;
                case 2:
                    il.Emit(OpCodes.Ldc_I4_2);
                    return;
                case 3:
                    il.Emit(OpCodes.Ldc_I4_3);
                    return;
                case 4:
                    il.Emit(OpCodes.Ldc_I4_4);
                    return;
                case 5:
                    il.Emit(OpCodes.Ldc_I4_5);
                    return;
                case 6:
                    il.Emit(OpCodes.Ldc_I4_6);
                    return;
                case 7:
                    il.Emit(OpCodes.Ldc_I4_7);
                    return;
                case 8:
                    il.Emit(OpCodes.Ldc_I4_8);
                    return;
            }

            if (value > -129 && value < 128)
            {
                il.Emit(OpCodes.Ldc_I4_S, (SByte)value);
            }
            else
            {
                il.Emit(OpCodes.Ldc_I4, value);
            }
        }
        #endregion
        #endregion
    }

    internal static class ExtensionCache
    {   
        #region 存入Cache   
        /// <summary>   
        ///  存入Cache   
        /// </summary>    
        /// <param name="key">缓存key</param>  
        /// <param name="value">缓存的值</param> 
        /// <param name="timeMm">存xx分钟</param>  
        /// <returns>是否执行成功[bool]</returns>  
        public static bool SetCache(string key, object value, int timeMm)   
        {      
            bool result = false;       
            try      
            {
                TimeSpan ts = new TimeSpan(0, timeMm, 0); 
                System.Web.HttpRuntime.Cache.Insert(key, 
                    value,
                    null,           
                    System.Web.Caching.Cache.NoAbsoluteExpiration,
                     ts);       
                result = true;  
            }      
            catch { }       
            return result;   
        }   
        #endregion  
        #region 取得Cache   
        /// <summary>    
        /// 取得Cache   
        /// /// </summary>   
        /// /// <param name="key">key</param>   
        /// /// <returns>object类型</returns>   
        public static T GetCache<T>(string key)  
        {       
            return (T)System.Web.HttpRuntime.Cache.Get(key);   
        }   
        #endregion 
        #region 查询Cache是否存在   
        /// <summary>    ///
        /// 查询Cache是否存在    ///
        /// </summary>    /// 
        /// <param name="key">key 值</param>    
        /// <returns>bool</returns>   
        public static bool IsCacheExist(string key)   
        {      
            bool result = false;     
            object temp = System.Web.HttpRuntime.Cache.Get(key);   
            if (null != temp)    
            {         
                result = true; 
            }      
            return result;  
        }  
        #endregion
    }
}
